package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.sky.entity.*;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Collection;

/*
* 设置自定义切入类
* */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    // 切入点：拦截 mapper 中带 @AutoFill 注解的方法
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillpointCut() {}

    @Before("autoFillpointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段自动填充");

        // 1. 获取操作类型（INSERT/UPDATE）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType operationType = autoFill.value();

        // 2. 获取方法参数
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }

        // 3. 准备填充数据（当前时间 + 当前登录用户 ID）
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        // 4. 遍历所有参数，处理「单个实体」和「集合实体」
        for (Object arg : args) {
            if (arg == null) {
                continue;
            }

            // 4.1 如果参数是集合（如 List<SetmealDish>），遍历集合中的每个实体
            if (arg instanceof Collection<?>) {
                Collection<?> collection = (Collection<?>) arg;
                for (Object entity : collection) {
                    fillCommonFields(entity, operationType, now, currentId);
                }
            }
            // 4.2 如果参数是单个实体类（如 Setmeal、SetmealDish），直接填充
            else {
                fillCommonFields(arg, operationType, now, currentId);
            }
        }
    }

    /**
     * 核心：给单个实体类填充公共字段（createTime/updateTime 等）
     * @param entity 单个实体类对象（如 SetmealDish）
     * @param operationType 操作类型（INSERT/UPDATE）
     * @param now 当前时间
     * @param currentId 当前登录用户 ID
     */
    private void fillCommonFields(Object entity, OperationType operationType, LocalDateTime now, Long currentId) {
        try {
            // 5. 反射获取实体类的 setter 方法（避免直接在循环中重复获取）
            Method setCreateTime = getDeclaredMethod(entity, AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method setCreateUser = getDeclaredMethod(entity, AutoFillConstant.SET_CREATE_USER, Long.class);
            Method setUpdateTime = getDeclaredMethod(entity, AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
            Method setUpdateUser = getDeclaredMethod(entity, AutoFillConstant.SET_UPDATE_USER, Long.class);

            // 6. INSERT 操作：填充 createTime/createUser + updateTime/updateUser
            if (operationType == OperationType.INSERT) {
                if (setCreateTime != null) setCreateTime.invoke(entity, now);
                if (setCreateUser != null) setCreateUser.invoke(entity, currentId);
            }

            // 7. INSERT/UPDATE 操作：都填充 updateTime/updateUser
            if (setUpdateTime != null) setUpdateTime.invoke(entity, now);
            if (setUpdateUser != null) setUpdateUser.invoke(entity, currentId);

        } catch (Exception e) {
            log.error("公共字段自动填充失败", e);
            throw new RuntimeException("公共字段自动填充失败", e);
        }
    }

    /**
     * 工具方法：安全获取实体类的声明方法（避免找不到方法时报错）
     * @param entity 实体对象
     * @param methodName 方法名
     * @param parameterTypes 方法参数类型
     * @return 找到的方法，找不到返回 null
     */
    private Method getDeclaredMethod(Object entity, String methodName, Class<?>... parameterTypes) {
        try {
            // getDeclaredMethod：获取类自身声明的方法（包括私有方法）
            Method method = entity.getClass().getDeclaredMethod(methodName, parameterTypes);
            method.setAccessible(true); // 允许访问私有方法（如果有需要）
            return method;
        } catch (NoSuchMethodException e) {
            // 实体类没有该方法（如某些实体不需要填充该字段），返回 null 不报错
            log.warn("实体类 {} 没有方法 {}", entity.getClass().getName(), methodName);
            return null;
        }
    }
}